package com.hyt.it.ogt.kq.service.gov.schedule.strategy;

import java.util.*;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import com.hyt.it.ogt.kq.service.gov.arrange.model.Proctor;
import com.hyt.it.ogt.kq.service.gov.schedule.impl.ExamRoomScheduleImpl;
import com.hyt.it.ogt.kq.service.gov.schedule.model.Candidate;
import com.hyt.it.ogt.kq.service.gov.schedule.model.ExamInfo;
import com.hyt.it.ogt.kq.service.gov.schedule.model.ExamRoom;
import com.hyt.it.ogt.kq.service.gov.schedule.model.ScheduleConstrain;
import com.hyt.it.ogt.kq.service.gov.schedule.model.ScheduleExamRoom;
import com.hyt.it.ogt.kq.service.gov.schedule.model.ScheduleResult;
import com.hyt.it.ogt.kq.service.gov.schedule.model.ScheduleExamRoom.ScheduleCandidate;
import com.hyt.it.ogt.kq.service.gov.schedule.model.ScheduleRecord;
import com.hyt.it.ogt.kq.service.gov.schedule.utils.ExamRoomUtils;
import com.hyt.it.ogt.kq.service.gov.schedule.utils.FineTuneUtils;
import com.hyt.it.ogt.kq.service.gov.schedule.utils.ScheduleUtils;
import com.hyt.it.ogt.kq.service.gov.schedule.utils.SupportUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/***
 * 策略编排
 */
public interface ScheduleStrategy {
        static final Logger logger = LoggerFactory.getLogger(ScheduleStrategy.class);

        /***
         * 编排入口
         * 
         * @param scheduleConstrain
         * @param examInfos         批次考试信息
         * @param groupName         分组名
         * @param batchNoList       批次列表
         * @param examRooms         编排考场
         * @param virCandidateIdList   编排虚拟考生ID
         * @return
         */
        public default List<ScheduleResult> apply(ScheduleConstrain scheduleConstrain, List<ExamInfo> examInfos,
                        String groupName,
                        List<String> batchNoList,
                        List<ExamRoom> examRooms, List<String> virCandidateIdList) {

                // 考生信息 <批次号, <考生ID, 考生>>
                Map<String, Map<String, Candidate>> examCandidateMap = ScheduleUtils.getExamCandidateMap(examInfos);
                // 虚拟考生ID， 考生对应表 <虚拟考生ID, <批次号, 考生ID>>
                Map<String, Map<String, String>> virId2CandidateIdMap = ScheduleHelper.getInstance()
                                .getVirId2CandidateIdMap();

                // 虚拟考生ID对应需考试的科目
                Map<String, List<String>> virId2ExamSubjectsMap = ScheduleHelper.getInstance()
                                .getVirId2ExamSubjectsMap();

                // <考场ID, 考场>
                Map<String, ExamRoom> examRoomMap = ScheduleHelper.getInstance().getExamRoomMap();

                List<String> virCandidateIds = null;
                if (!scheduleConstrain.isSingleSubject()) {
                        // 将虚拟考生ID，按其虚拟考生所占科目数进行排序
                        virCandidateIds = virCandidateIdList.stream().sorted(Comparator.comparingInt(
                                        item -> (SupportUtils.countVirCandidateSubject(virId2ExamSubjectsMap,
                                                        (String) item)))
                                        .reversed())
                                        .collect(Collectors.toList());
                } else {
                        // 将虚拟考生ID，先按其虚拟考生所占科目数进行排序，再按考试科目排序
                        virCandidateIds = virCandidateIdList.stream().sorted(Comparator.comparingInt(
                                        item -> (SupportUtils.countVirCandidateSubject(virId2ExamSubjectsMap,
                                                        (String) item)))
                                        .reversed()
                                        .thenComparing(Comparator
                                                        .comparing(item -> (SupportUtils.mergeVirCandidateSubject(
                                                                        virId2ExamSubjectsMap, (String) item)))))
                                        .collect(Collectors.toList());

                }

                // 开始编排，返回对应的编著排结果列表
                List<ScheduleRecord> scheduleRecords = this.schedule(scheduleConstrain, groupName, batchNoList,
                                examRooms,
                                virCandidateIds);
                List<ScheduleResult> scheduleResults = new ArrayList<>();

                // 按批次号，将编排后的考生虚拟ID对应的相应的批次的考生ID
                for (String batchNo : batchNoList) {
                        Map<String, ScheduleExamRoom> scheduleExamRoomMap = new HashMap<>();
                        for (ScheduleRecord scheduleRecord : scheduleRecords) {
                                String candidateId = virId2CandidateIdMap.get(scheduleRecord.getVirCandidateId())
                                                .get(batchNo);
                                if (candidateId == null) {
                                        continue;
                                }

                                if (!scheduleExamRoomMap.containsKey(scheduleRecord.getRoomId())) {
                                        ExamRoom examRoom = examRoomMap.get(scheduleRecord.getRoomId());
                                        ScheduleExamRoom scheduleExamRoom = ScheduleExamRoom.builder()
                                                        .roomId(examRoom.getRoomId()).groupName(groupName)
                                                        .placeId(examRoom.getPlaceId())
                                                        .capacity(examRoom.getCapacity())
                                                        .timeRoomId(examRoom.getRoomTimeId())
                                                        .province(examRoom.getProvince())
                                                        .city(examRoom.getCity())
                                                        .district(examRoom.getDistrict())
                                                        .subjects(new HashSet<>())
                                                        .candidates(new ArrayList<>())
                                                        .build();
                                        scheduleExamRoomMap.put(examRoom.getRoomId(), scheduleExamRoom);
                                }

                                Candidate candidate = examCandidateMap.get(batchNo).get(candidateId);
                                ScheduleCandidate scheduleCandidate = ScheduleCandidate.builder()
                                                .position(scheduleRecord.getPosition())
                                                .roomId(scheduleRecord.getRoomId())
                                                .subject(candidate.getSubject())
                                                .candidateSubjectId(candidate.getCandidateSubjectId())
                                                .bmStudentId(candidate.getBmStudentId())
                                                .build();
                                scheduleExamRoomMap.get(scheduleRecord.getRoomId()).getCandidates()
                                                .add(scheduleCandidate);
                        }
                        ScheduleResult scheduleResult = ScheduleResult.builder()
                                        .examRooms(new ArrayList<>(scheduleExamRoomMap.values()))
                                        .batchNo(batchNo)
                                        .scheduleTaskId(scheduleConstrain.getScheduleTaskId()).build();
                        scheduleResults.add(scheduleResult);
                }

                List<ScheduleResult> finalScheduleResults = this.fineTune(scheduleConstrain, scheduleResults);
                return finalScheduleResults;
        }




        // 编排处理
        List<ScheduleRecord> schedule(ScheduleConstrain scheduleConstrain, String groupName,
                        List<String> batchNoList, List<ExamRoom> examRooms, List<String> virCandidateIds);

        /***
         * 将虚拟考生填充到考场
         *
         * @param scheduleConstrain
         * @param examRooms
         * @param virCandidateIds
         * @return
         */
        public default List<ScheduleRecord> fillExamRoom(ScheduleConstrain scheduleConstrain, List<ExamRoom> examRooms,
                        List<String> virCandidateIds) {
                List<ScheduleRecord> scheduleRecords = new ArrayList<>();
                Map<String, List<String>> virId2ExamSubjectsMap = ScheduleHelper.getInstance()
                                .getVirId2ExamSubjectsMap();
                int count = 0;
                for (String virCandidateId : virCandidateIds) {
                        if (count % 100 == 0) {
                                logger.info("filling candidate to room , current number {}, total number {}", count,
                                                virCandidateIds.size());
                        }
                        count = count + 1;
                        // 取得虚拟考生所有批次的考试科目
                        List<String> candidateExamSubjects = virId2ExamSubjectsMap.get(virCandidateId);
                        ExamRoom examRoom = null;

                        if (!scheduleConstrain.isSingleSubject()) {
                                // 混合科目选择考场， 根据虚拟考生的考试科目选择包含这些科目的考场
                                examRoom = ExamRoomUtils.findCanScheduleExamRoomBySubjects(examRooms,
                                                candidateExamSubjects);
                        } else {
                                // 单科目选择考场， 根据虚拟考生的考试科目选择包含这些科目的考场， 同时需检测该考场已有
                                // 考生的考试科目（需要相同）
                                examRoom = ExamRoomUtils.findCanScheduleExamRoomByAllSubjects(examRooms,
                                                candidateExamSubjects);
                        }

                        if (examRoom == null) {
                                continue;
                        }
                        ScheduleRecord scheduleRecord = ScheduleRecord.builder().roomId(examRoom.getRoomId())
                                        .position(examRoom.getIdlePosition().pop()).virCandidateId(virCandidateId)
                                        .build();
                        // 根据虚拟考生的考试科目，更新考场的批次考试科目信息
                        examRoom.mergeBatchSubjectList(candidateExamSubjects);
                        scheduleRecords.add(scheduleRecord);
                }
                return scheduleRecords;
        }

        /***
         * 对编排结果进行微调，暂时主要针对单科目, 不保持考生座位的方式进行微调
         * 
         * @param scheduleConstrain
         * @param scheduleResults
         * @return
         */
        default List<ScheduleResult> fineTune(ScheduleConstrain scheduleConstrain,
                        List<ScheduleResult> scheduleResults) {
                // 对单科目, 不保持考生座位进行微调
                if (scheduleConstrain.isSingleSubject() && (!scheduleConstrain.isMainTainPosition())) {
                        List<ScheduleResult> fineTuneResults = new ArrayList<>();
                        for (ScheduleResult preScheduleResult : scheduleResults) {
                                ScheduleResult fineTuneResult = ScheduleResult.builder()
                                                .examRooms(FineTuneUtils.fineTuneSingleAndNoMainTen(
                                                                preScheduleResult.getExamRooms()))
                                                .batchNo(preScheduleResult.getBatchNo())
                                                .scheduleTaskId(preScheduleResult.getScheduleTaskId()).build();
                                fineTuneResults.add(fineTuneResult);
                        }
                        return fineTuneResults;
                } else {
                        return scheduleResults;
                }
        }

}
